// ==================================================================================
// Shared Genomics Project MPI Codebase
// Version 1.0 30/04/2010
//
// (c) 2010 University of Manchester all rights reserved
//
// This file is distributed under the GNU General Public License, Version 2.  
// Please see the file COPYING.txt for more details
// ==================================================================================

/*!
\file 
\brief Implementation of the memory allocation utility functions
*/

#include <ctype.h>
#include <string.h>
#include <math.h>
#include <stdio.h>
#include <stdlib.h>

#include "utils.h"

int create_array(void** dst, int datatype_size, int nElements) {
	if (dst != NULL && datatype_size > 0 && nElements > 0) {
		*dst = malloc(datatype_size * nElements);
		return *dst != NULL;
	}

	return 0;
}

// Writing error stream, echo to STDOUT for moment.
int error(const char* message) {
	if (message != NULL) {
		fprintf(stderr, "ERROR: %s\n", message);
		return 1;
	}

	return 0;
}

int nullInitStringArray(char **arr, int nRows) {
	int i;

	if (arr != NULL) {
		for (i = 0; i < nRows; i++) 
			arr[i] = '\0';

		return 1;
	}

	return 0;
}

int print_log(const char* message) {
	if (message != NULL) {
		fprintf(stdout, "%s\n", message);
		return 1;
	}

	return 0;
}

BOOL read_string(FILE * fin, char *buffer, int buffer_length)
{
	BOOL readData = FALSE;
	int nRead = 0;

	memset(buffer, '\0', buffer_length); // Reset the buffer
	while (nRead < buffer_length - 1)
	{
		char ch = (char) fgetc(fin);
		if (ch == EOF) break;
		else if (isalnum(ch) || ispunct(ch)) {
			*buffer = ch;
			buffer++;
			nRead++;
		} else {
			break;
		}
	}

	if (nRead > 0) 
		readData = TRUE;

	return readData;
}

BOOL* create_bool_array(int size, BOOL initial_value) {
	BOOL *pValue = NULL;

	if (size > 0) {
		pValue = (BOOL*) malloc(sizeof(BOOL) * size);
		if (pValue != NULL) {
			int i;
			for (i = 0; i < size; i++) pValue[i] = initial_value;
		}
	}

	return pValue;
}

VECTOR_T create_vector(int size, double initial_value) {
	VECTOR_T pValue = NULL;

	if (size > 0) {
		pValue = (VECTOR_T) malloc(sizeof(double) * size);
		if (pValue != NULL) {
			int i;
			for (i = 0; i < size; i++) pValue[i] = initial_value;
		}
	}

	return pValue;
}

long int** create_2d_long_int_array(int rows, int max_width, long int initial_value) {
	long int** pValue = NULL;
	int nRowsAllocated = 0, i, j;
	BOOL allRowsAllocated = FALSE;

	if (rows > 0 && max_width > 0) {
		pValue = (long int**) malloc(sizeof(long int*) * rows);
		if (pValue == NULL) return NULL;
		else for (i = 0; i < rows; i++) pValue[i] = NULL;

		// Allocate the row arrays
		for (i = 0; i < rows; i++) {
			pValue[i] = (long int*) malloc(sizeof(long int) * max_width);
			if (pValue[i] != NULL) {
				for (j = 0; j < max_width; j++) pValue[i][j] = initial_value;
				nRowsAllocated++;
			} else break;
		}
		allRowsAllocated = (nRowsAllocated == rows) ? TRUE : FALSE;
		if (!allRowsAllocated) {
			for (i = 0; i < nRowsAllocated; i++) free(pValue[i]);
			free(pValue);
			pValue = NULL;
		}
	}

	return pValue;
}

int* create_int_array(int size, int initial_value) {
	int *pValue = NULL;

	if (size > 0) {
		pValue = (int*) malloc(sizeof(int) * size);
		if (pValue != NULL) {
			int i;
			for (i = 0; i < size; i++) pValue[i] = initial_value;
		}
	}

	return pValue;
}

MATRIX_T create_matrix(int rows, int columns, double initial_value) {
	MATRIX_T m = NULL;
	double *pNumbers = NULL;
	double **pRows = NULL;
	long int nElements = rows * columns;

	if (rows > 0 && columns > 0) {
		pNumbers = (double*) malloc(nElements * sizeof(double));
		if (pNumbers != NULL) {
			pRows = (double**) malloc(rows * sizeof(double*));
			if (pRows != NULL) {
				register long int i;
				for (i = 0; i < nElements; i++) pNumbers[i] = initial_value;
				for (i = 0; i < rows; i++) pRows[i] = pNumbers + (columns * i);

				m = pRows;
			} else {
				free(pNumbers);
				m = NULL;
			}
		} 
	}

	return m;
}

void zero_vector(VECTOR_T v, int cols) { 
	int i;
	for (i = 0; i < cols; i++) v[i] = 0;
}

void zero_matrix(MATRIX_T m, int rows, int cols) { 
	int i, j;

	for (i = 0; i < rows; i++) {
		for (j = 0; j < cols; j++) m[i][j] = 0;
	}
}

void free_matrix(MATRIX_T *arr) {
	if (*arr != NULL) {
		if (*arr[0] != NULL) free(*arr[0]);
		free(*arr);
		*arr = NULL;
	}
}

void set_vector(VECTOR_T v, int cols, double value) { 
	int i;
	for (i = 0; i < cols; i++) v[i] = value;
}

void set_datafile_path(char *buffer, char *dir, char *runId, int rank, char *suffix) {
	sprintf(buffer, "%s%s_%i%s", dir, runId, rank, suffix);
}

// Look at a data directory, and from a given file suffix tries to get the 
// header "numprocs" value from a file.
int readNumberOfProcs(char *dst, char *line, int line_length, char *dir, char *runId, char *suffix) {
	int numprocs = -1, i;

	// Load a epi out file, if found, read the first line
	// and get the number of processors involved in the calculation.
	for (i = 0; i < 10; i++) {
		FILE *in = NULL;

		set_datafile_path(dst, dir, runId, i, suffix);
		in = fopen(dst, "r");
		if (in != NULL) {
			fgets(line, line_length, in);
			fclose(in);
			numprocs = atoi(line);
			if (numprocs > 0) break;
		}
	} 

	return numprocs;
}

#ifndef WIDE_LINE_LENGTH
#define WIDE_LINE_LENGTH 131072
#endif // WIDE_LINE_LENGTH

char** load_file(char *filepath, int *nLines) {
	FILE *in = NULL;
	char **lines = NULL;
	char line[WIDE_LINE_LENGTH]; // Use a wide buffer initially to get the maximum line width.
	size_t maxLineLength = 0;
	int _nLines = 0, lineCount = 0;

	if (filepath == NULL || nLines == NULL) return lines;
	else if (strlen(filepath) == 0) return lines;
	
	// Read in the input text file & dump into a suitablely sized buffer.
	*nLines = 0;
	in = fopen(filepath, "r");
	if (in != NULL) {
		while(fgets(line, WIDE_LINE_LENGTH, in)) {
			size_t lineLength =  strlen(line);
			if (lineLength > maxLineLength) maxLineLength = lineLength;
			_nLines++;
		}
		fclose(in);

		if (maxLineLength > 0) {
			lines = create_2d_char_array(_nLines, maxLineLength + 1);
			if (lines != NULL) {
				in = fopen(filepath, "r");
				if (in != NULL) {
					int i;
					for (i = 0; i < _nLines; i++) {
						if(fgets(line, WIDE_LINE_LENGTH, in)) {
							strcpy(lines[i], line);
							lineCount++;
						}
					}
					fclose(in);
					*nLines = lineCount;
				}
			}
		}
	}

	return lines;
}

char** create_2d_char_array(size_t nRows, size_t row_length) {
	char **lines = NULL;

	if (nRows > 0 && row_length > 0) {
		lines = (char**) malloc(sizeof(char**) * nRows);
		if (lines != NULL) {
			unsigned int i, linesAllocated = 0;

			// Initialise strings to NULL
			for (i = 0; i < nRows; i++) {
				lines[i] = (char*) malloc(sizeof(char) * row_length);
				if (lines[i] != NULL) {
					lines[i][0] = '\0';
					linesAllocated++;
				}
			}

			// If not all the lines allocated as out of heap, dealloc what's been assigned
			// and set the return array to NULL to flag an error.
			if (linesAllocated != nRows) {
				for (i = 0; i < linesAllocated; i++) {
					if (lines[i] != NULL) free(lines[i]);
				}
				free(lines);
				lines = NULL;
			}
		}
	}

	return lines;
}

#ifndef TOKEN_LENGTH
#define TOKEN_LENGTH 80
#endif 
int get_number_of_tokens(char *str, char delimiter) {
	int n = 0;
	char token[TOKEN_LENGTH];
	char *p, *q;

	if (str == NULL) return n;
	else if (strlen(str) == 0) return n;

	p = str;
	while (*p) {
		q = token;
		while ((*p != delimiter && *p != '\r' && *p != '\n') && *p) {
			*q = *p;
			p++;
			q++;
		}
		if (*p) p++; // Go past the whitespace.
		*q = '\0'; // NULL terminate the token
		if (strlen(token) > 0) n++;
	}

	return n;
}

BOOL get_token(char *src, char *dst, int index, char delimiter) {
	int tokenCount = 0;
	char *p, *q;
	BOOL foundToken = FALSE;

	if (src == NULL || dst == NULL || index < 0) return foundToken;

	// Read the required token from string.
	p = src;
	while (*p) {
		q = dst;
		while ((*p != delimiter && *p != '\r' && *p != '\n') && *p) {
			*q = *p;
			p++;
			q++;
		}
		if (*p) p++; 
		*q = '\0';
		tokenCount++;
		if ((tokenCount - 1) == index) {
			foundToken = TRUE;
			break;
		}
	}

	return foundToken;
}

BOOL get_column(char **src, char **dst, int nRows, int index, BOOL hasHeader, char delimiter) {
	BOOL allOkay = TRUE;
	int i;

	if (src == NULL || dst == NULL || nRows < 0 || index < 0) return FALSE;

	zero_2d_char_array(dst, nRows);
	for (i = 0; i < nRows; i++) {
		if (i == 0 && hasHeader) continue;
		if(!get_token(src[i], dst[i], index, delimiter)) {
			allOkay = FALSE;
			break;
		} 
	}

	return allOkay;
}

void zero_2d_char_array(char **src, int nRows) {
	if (src != NULL && nRows > 0) {
		int i;
		for (i = 0; i < nRows; i++) *src[i] = '\0';
	}
}

BOOL get_levels(char **src, int nSrc, char **dst, int nDst, int *nLevels, BOOL allowEmptyStrings) {
	int i, j, levelCount = 0;

	if (src == NULL || nSrc < 0 || dst == NULL || nDst < 0 || nLevels == NULL) return FALSE;

	// to start the comparison loops.
	zero_2d_char_array(dst, nDst);
	*nLevels = 0;

	// copy 1st element from the src array into dst as need something
	// to  start with when getting the levels from the array.
	for (i = 0; i < nSrc; i++) {
		if (strlen(src[i]) > 0) {
			strcpy(dst[0], src[i]);
			levelCount++;
			break;
		}
	}

	// Compare all values to get the levels, if the maximum values reached assume
	// column is a continuous data type.
	for (i = 0; i < nSrc; i++) {
		BOOL isFound = FALSE;

		if (!allowEmptyStrings) {
			if (strlen(src[i]) == 0) continue;
		}
		if (levelCount == nDst) break; // Hit max. levels.

		for (j = 0; j < levelCount; j++) {
			if(strcmp(src[i], dst[j]) == 0) {
				isFound = TRUE;
				break;
			}
		}
		if (!isFound) {
			strcpy(dst[levelCount], src[i]);
			levelCount++;
		}
	}
	*nLevels = levelCount;

	// If level count hit's max, this means that the column value ids likely to be
	// a continous field and so doesn't have levels.
	if (levelCount == nDst) {
		*nLevels = 0;
		return FALSE;
	} else {
		qsort(dst,levelCount, sizeof(char*),cstring_cmp);
		return TRUE;
	}
}

int cstring_cmp(const void *a, const void *b)
{
	const char **ia = (const char **) a;
	const char **ib = (const char **) b;
	return strcmp(*ia, *ib);
}

int free_2d_char_array(char **src, size_t nRows) {
	if (src != NULL && nRows > 0) {
		size_t i;
		for (i = 0; i < nRows; i++) if (src[i] != NULL) free(src[i]);
		free(src);

		return 1;
	}

	return 0;
}

void zero_int_array(int *src, int nSrc) {
	int i;
	if (src != NULL && nSrc > 0) for (i = 0; i < nSrc; i++) src[i] = 0;
}

void echo_int_array(int *src, int nSrc) {
	int i;
	if (src != NULL && nSrc > 0) for (i = 0; i < nSrc; i++) printf("%i ", src[i]);
	printf("\n");
}

int** create_2d_int_array(int rows, int max_width, int initial_value) {
	int** pValue = NULL;
	int nRowsAllocated = 0, i, j;
	BOOL allRowsAllocated = FALSE;

	if (rows > 0 && max_width > 0) {
		pValue = (int**) malloc(sizeof(int*) * rows);
		if (pValue == NULL) return NULL;
		else for (i = 0; i < rows; i++) pValue[i] = NULL;

		// Allocate the row arrays
		for (i = 0; i < rows; i++) {
			pValue[i] = (int*) malloc(sizeof(int) * max_width);
			if (pValue[i] != NULL) {
				for (j = 0; j < max_width; j++) pValue[i][j] = initial_value;
				nRowsAllocated++;
			} else break;
		}
		allRowsAllocated = (nRowsAllocated == rows) ? TRUE : FALSE;
		if (!allRowsAllocated) {
			for (i = 0; i < nRowsAllocated; i++) free(pValue[i]);
			free(pValue);
			pValue = NULL;
		}
	}

	return pValue;
}

void free_2d_int_array(int **src, int nRows) {
	if (src != NULL && nRows >0) {
		int i;
		for (i = 0; i < nRows; i++) if (src[i] != NULL) free(src[i]);
		free(src);
	}
}

void echo_2d_char_array(char **src, int nRows) {
	if (src != NULL && nRows > 0) {
		int i;
		for (i = 0; i < nRows; i++) fprintf(stdout, "value=[%s]\n", src[i]);
		fprintf(stdout, "\n");
	}
}

void echo_matrix(MATRIX_T u, int nRows, int nCols) {
	int i, j;

	if (u == NULL || nRows < 1 || nCols < 1) {
		fprintf(stderr, "WARNING: Matrix not sized\n");
		return;
	}
	for (i = 0; i < nRows; i++) {
		printf("%i)\t", i);
		for (j = 0; j < nCols; j++) {
			printf("%10.8f ", u[i][j]);
		}
		printf("\n");
	}
	printf("\n");
}

void echo_vector(VECTOR_T u, int nCols) {
	int i;

	if (u == NULL) return;

	for (i = 0; i < nCols; i++) {
		printf("%i)\t", i);
		printf("%10.8f\n", u[i]);
	}
	printf("\n\n\n");
}

void init_int_array(int *src, int size, int value) {
	if (src != NULL && size > 0) {
		int i;
		for (i = 0; i < size; i++) src[i] = value;
	}
}

// qsort int comparison function
int int_cmp(const void *a, const void *b)
{
	const int *ia = (const int*) a; 
	const int *ib = (const int*) b;

	return *ia  - *ib; 
}

// 2 integers on 1st line, matrix dimensions.
// The rest is just memory allocation & streaming.
#define MAX_MATRIX_LINE_LENGTH 1024
MATRIX_T read_matrix(char *file, int *nRows, int *nCols) {
	FILE *in = NULL;
	MATRIX_T a = NULL;
	int m, n;
	char dummy[MAX_MATRIX_LINE_LENGTH];
	
	if (strlen(file) == 0) return a;
	else if (nRows == NULL || nCols == NULL) return a;

	in = fopen(file, "r");
	if (in == NULL) return a;  // Failed to open fail.

	*nRows = *nCols = 0; // Set the values as zero, just incase.
	fgets(dummy, MAX_MATRIX_LINE_LENGTH, in);
	sscanf(dummy, "%i %i", &m, &n);

	if (!(m > 0 && n > 0)) {
		if (in != NULL) fclose(in);
		return a;
	} else {
		a = create_matrix(m, n, 0);
		if (a != NULL) {
			int i, j;
			*nRows = m;
			*nCols = n;
			for (i = 0; i < m; i++) {
				char token[80];
				fgets(dummy, MAX_MATRIX_LINE_LENGTH, in);
				for (j = 0; j < n; j++) {
					get_token(dummy, token, j, ' ');
					a[i][j] = atof(token);
				}
			}
		} else {
		}
	}
	if (in != NULL) fclose(in);

	return a;
}

void init_bool_array(BOOL *src, int size, BOOL value) {
	if (src != NULL && size > 0) {
		int i;
		for (i = 0; i < size; i++) src[i] = value;
	}
}

int* read_int_array(char* filepath, int *size) {
	int *arr = NULL;

	if (size != NULL) *size = 0;

	if (filepath == NULL || size == NULL) return arr;
	else if (strlen(filepath) == 0) return arr;
	else {
		FILE *in  = fopen(filepath, "r");
		if (in != NULL) {
			int _size = 0;

			if (fscanf(in, "%i", &_size) != EOF) {
				if (_size > 0) {
					arr = create_int_array(_size, 0);
					if (arr != NULL) {
						BOOL hasReadAll = TRUE;
						int i;
						for (i = 0; i < _size; i++) {
							if (fscanf(in, "%i", &arr[i]) == EOF) {
								hasReadAll = FALSE;
								break;
							}
						}
						if (!hasReadAll) { // I/O failure so set output to NULL.
							free(arr);
							arr = NULL;
						} else *size = _size;
					}
				}
			}
			fclose(in);
		}
	}

	return arr;
}

BOOL** create_2d_bool_array(int rows, int columns, BOOL initial_value) {
	BOOL **m = NULL;
	BOOL *pNumbers = NULL;
	BOOL **pRows = NULL;
	long int nElements = rows * columns;

	if (rows > 0 && columns > 0) {
		pNumbers = (BOOL*) malloc(nElements * sizeof(BOOL));
		if (pNumbers != NULL) {
			pRows = (BOOL**) malloc(rows * sizeof(BOOL*));
			if (pRows != NULL) {
				register long int i;
				for (i = 0; i < nElements; i++) pNumbers[i] = initial_value;
				for (i = 0; i < rows; i++) pRows[i] = pNumbers + (columns * i);
				m = pRows;
			} else {
				free(pNumbers);
				m = NULL;
			}
		} 
	}

	return m;
} 

void free_2d_bool_array(BOOL **arr) {
	if (arr != NULL) {
		if (arr[0] != NULL) free(arr[0]);
		free(arr);
	}
}

void fill_indices(int *src, int nSrc) {
	if (src == NULL || nSrc <= 0) return;
	else {
		int i;
		for (i = 0; i < nSrc; i++) src[i] = i;
	}
}

char* create_char_array(int size, char initial_value) {
	int i;
	char *arr = NULL;

	if (size > 0) {
		arr = (char*) malloc(sizeof(char) * size);
		if (arr != NULL) {
			for (i = 0; i < size; i++) arr[i] = initial_value;
		}
	}

	return arr;
}

void echo_bool_array(BOOL *src, int nSrc) {
	if (src!= NULL && nSrc > 0) {
		int i;
		for (i = 0; i < nSrc; i++) fprintf(stdout, "%i ", src[i]);
		fprintf(stdout, "\n");
	}
}

int set_all_lowercase(char **src, int nRows) {
	int i, j; 

	if (src == NULL || nRows <= 0) return 0;

	for (i = 0; i < nRows; i++) {
		size_t length = strlen(src[i]);
		if (length > 0) for (j = 0; j < length; j++) src[i][j] = (char) tolower(src[i][j]);
	}

	return 1;
}

int double_cmp(const void *a, const void *b)
{
	const double *ia = (const double*) a; 
	const double *ib = (const double*) b;

	if (*ia > *ib) return 1;
	else if (*ia < *ib) return -1;
	else return 0;
}
